package cn.texous.web.parsing.book.service.impl.mysql;

import cn.texous.util.commons.util.CollectionUtils;
import cn.texous.util.commons.util.GsonUtils;
import cn.texous.web.parsing.book.common.constants.NormalStatusEnum;
import cn.texous.web.parsing.book.common.constants.OriginDetailRequestTypeEnum;
import cn.texous.web.parsing.book.common.constants.OriginDetailTypeEnum;
import cn.texous.web.parsing.book.config.mysql.AbstractService;
import cn.texous.web.parsing.book.dao.mysql.AnaOriginDetailFieldMapper;
import cn.texous.web.parsing.book.dao.mysql.AnaOriginDetailMapper;
import cn.texous.web.parsing.book.dao.mysql.AnaOriginDetailRequestMapper;
import cn.texous.web.parsing.book.dao.mysql.AnaOriginMapper;
import cn.texous.web.parsing.book.model.entity.mysql.AnaOrigin;
import cn.texous.web.parsing.book.model.entity.mysql.AnaOriginDetail;
import cn.texous.web.parsing.book.model.entity.mysql.AnaOriginDetailField;
import cn.texous.web.parsing.book.model.entity.mysql.AnaOriginDetailRequest;
import cn.texous.web.parsing.book.model.parser.origin.AnaOriginParser;
import cn.texous.web.parsing.book.model.parser.origin.AnaOriginParserBaseInfo;
import cn.texous.web.parsing.book.model.parser.origin.AnaOriginParserFieldInfo;
import cn.texous.web.parsing.book.model.parser.origin.AnaOriginParserModuleInfo;
import cn.texous.web.parsing.book.model.parser.origin.AnaOriginParserRequestInfo;
import cn.texous.web.parsing.book.model.vo.AnaOriginDictVO;
import cn.texous.web.parsing.book.service.api.mysql.AnaOriginService;
import com.google.gson.reflect.TypeToken;
import org.apache.commons.lang3.BooleanUtils;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;


/**
 * insert description here.
 *
 * @author Showa.L
 * @since 2020-03-03 16:17:56
 */
@Service
public class AnaOriginServiceImpl
        extends AbstractService<AnaOrigin> implements AnaOriginService {

    @Resource
    private AnaOriginMapper anaOriginMapper;
    @Resource
    private AnaOriginDetailMapper anaOriginDetailMapper;
    @Resource
    private AnaOriginDetailRequestMapper anaOriginDetailRequestMapper;
    @Resource
    private AnaOriginDetailFieldMapper anaOriginDetailFieldMapper;

    @Override
    public List<AnaOriginParser> buildOrigin() {
        // 获取 origin
        Map<String, Object> params = new HashMap<>();
        params.put("status", NormalStatusEnum.AVAILABLE.getCode());
        List<AnaOrigin> anaOrigins = anaOriginMapper.selectByParam(params);
        // 获取 origin detail
        List<AnaOriginDetail> anaOriginDetails = anaOriginDetailMapper.selectByParam(params);
        Map<Integer, List<AnaOriginDetail>> anaOriginDetailMap = anaOriginDetails.stream()
                .collect(Collectors.groupingBy(AnaOriginDetail::getAnaOriginId));
        // 获取 origin detail request
        List<AnaOriginDetailRequest> anaOriginDetailRequests = anaOriginDetailRequestMapper.selectByParam(params);
        Map<Integer, List<AnaOriginDetailRequest>> anaOriginDetailRequestMap = anaOriginDetailRequests.stream()
                .collect(Collectors.groupingBy(AnaOriginDetailRequest::getAnaOriginDetailId));
        // 获取 origin detail field
        List<AnaOriginDetailField> anaOriginDetailFields = anaOriginDetailFieldMapper.selectByParam(params);
        Map<Integer, List<AnaOriginDetailField>> anaOriginDetailFieldMap = anaOriginDetailFields.stream()
                .collect(Collectors.groupingBy(AnaOriginDetailField::getAnaOriginDetailId));

        List<AnaOriginParser> anaOriginParsers = new ArrayList<>();
        for (AnaOrigin anaOrigin : anaOrigins) {
            AnaOriginParserBaseInfo baseInfo = AnaOriginParserBaseInfo.builder()
                    .charset(anaOrigin.getCharset())
                    .customizeHeader(GsonUtils.fromJson(anaOrigin.getCustomizeHeader(),
                            new TypeToken<Map<String, String>>() {
                            }.getType()))
                    .datePattern(anaOrigin.getDatePattern())
                    .description(anaOrigin.getDescription())
                    .domain(anaOrigin.getDomain())
                    .dynamicAnalysis(BooleanUtils.toBoolean(anaOrigin.getDyn()))
                    .name(anaOrigin.getName())
                    .openCookies(BooleanUtils.toBoolean(anaOrigin.getOpenDookies()))
                    .openSSL(BooleanUtils.toBoolean(anaOrigin.getOpenHttps()))
                    .respType(anaOrigin.getRespType())
                    .retry(anaOrigin.getRetry())
                    .urlEncoder(anaOrigin.getUrlEncoder())
                    .weights(anaOrigin.getWeights())
                    .build();

            List<AnaOriginDetail> anaOriginDetailList = anaOriginDetailMap.get(anaOrigin.getId());
            List<AnaOriginParserModuleInfo> moduleInfos = new ArrayList<>();
            for (AnaOriginDetail anaOriginDetail : anaOriginDetailList) {
                List<AnaOriginDetailRequest> anaOriginDetailRequestList = anaOriginDetailRequestMap.get(anaOriginDetail.getId());
                List<AnaOriginParserRequestInfo> requestInfos = new ArrayList<>();
                for (AnaOriginDetailRequest anaOriginDetailRequest : anaOriginDetailRequestList) {
                    AnaOriginParserRequestInfo requestInfo = AnaOriginParserRequestInfo.builder()
                            .inCharset(anaOriginDetailRequest.getInCharset())
                            .method(anaOriginDetailRequest.getMethod())
                            .outCharset(anaOriginDetailRequest.getOutCharset())
                            .params(parseToMap(anaOriginDetailRequest.getParam()))
                            .url(anaOriginDetailRequest.getUrl())
                            .urlEncoder(anaOriginDetailRequest.getUrlEncoder())
                            .type(OriginDetailRequestTypeEnum.loadByCode(anaOriginDetailRequest.getType()))
                            .build();
                    requestInfos.add(requestInfo);
                }
                List<AnaOriginDetailField> anaOriginDetailFieldList = anaOriginDetailFieldMap.get(anaOriginDetail.getId());
                Map<Integer, List<AnaOriginDetailField>> fieldTreeMap = anaOriginDetailFieldList.stream()
                        .collect(Collectors.groupingBy(AnaOriginDetailField::getParentId));
                List<AnaOriginDetailField> roots = fieldTreeMap.get(0);
                List<AnaOriginParserFieldInfo> fieldInfos = parserFieldInfoTree(roots, fieldTreeMap, anaOriginDetail.getType());
                AnaOriginParserModuleInfo anaOriginParserModuleInfo = AnaOriginParserModuleInfo.builder()
                        .requestInfos(requestInfos)
                        .anaFieldInfos(fieldInfos)
                        .weights(anaOriginDetail.getWeights())
                        .dataPattern(anaOriginDetail.getDatePattern())
                        .retry(anaOriginDetail.getRetry())
                        .type(OriginDetailTypeEnum.loadByCode(anaOriginDetail.getType()))
                        .build();
                moduleInfos.add(anaOriginParserModuleInfo);
            }

            AnaOriginParser anaOriginParser = AnaOriginParser.builder()
                    .baseInfo(baseInfo)
                    .dataPattern(anaOrigin.getDatePattern())
                    .moduleInfos(moduleInfos)
                    .origin(anaOrigin.getCode())
                    .retry(anaOrigin.getRetry())
                    .weights(anaOrigin.getWeights())
                    .build();
            anaOriginParsers.add(anaOriginParser);
        }
        return anaOriginParsers;
    }

    @Override
    public List<AnaOriginDictVO> originList() {
        return anaOriginMapper.selectOriginList();
    }

    private List<AnaOriginParserFieldInfo> parserFieldInfoTree(
            List<AnaOriginDetailField> roots, Map<Integer,
            List<AnaOriginDetailField>> fieldTreeMap,
            Integer type) {
        if (CollectionUtils.isEmpty(roots))
            return null;
        List<AnaOriginParserFieldInfo> fieldInfos = new ArrayList<>();
        for (AnaOriginDetailField root : roots) {
            AnaOriginParserFieldInfo fieldInfo = AnaOriginParserFieldInfo.builder()
                    .changeNode(!BooleanUtils.toBoolean(root.getIsLeaf()))
                    .className(root.getClassName())
                    .field(root.getField())
                    .type(type)
                    .xPath(root.getXPath())
                    .childs(parserFieldInfoTree(fieldTreeMap.get(root.getId()), fieldTreeMap, type))
                    .build();
            fieldInfos.add(fieldInfo);
        }
        return fieldInfos;
    }

    private Map<String, String> parseToMap(String value) {
        return GsonUtils.fromJson(value,
                new TypeToken<Map<String, String>>() {
                }.getType());
    }
}
